#! /usr/bin/perl
#
#The stretagy in this file is :
        #1. Margin usage:   200USD  could buy 0.01 lots of EURUSD.
        #2. Max position number is 1.
        #4. Set limit and stop protection
	#5. Only open postion when EAM5>EMA10>EMA20>EMA40 or "less than"
    #6. Introduce moving stop protection  todo cmeng

# todo . Modify it.


use strict;
use Getopt::Long::Descriptive;
use Spreadsheet::ParseExcel;
use POSIX;

#usage:
my $helpText = "e.g. ./programName.pl --instrument=EURUSD --period=d1 --limit 100 --stop 100 ";
my ( $opt, $usage ) = describe_options(
    '%c %o <some-arg>',
    [ 'instrument|i=s', "EURUSD, GBPUSD, or etc", { default => "EURUSD" } ],
    [ 'period|p=s',     "D1,H4,H2,m30 or etc",    { default => "D1" } ],
    [],   
    [ 'limit|l=i',        "limit by pips", { default => 100 } ],
    [ 'stop|s=i',          "stop by pips",  { default => 100 } ],
    [ 'leveragefactor|lf=i', "leverage setting",  { default => 350 } ],
    [ 'initialmoney|im=i',   "initialmoney",  { default => 1000 } ],
    [ 'help|h',              $helpText ],
);

print( $usage->text ), exit if $opt->help;

#define some constants.
my $data_dir = "./forex_data";

#define global virables:
my %columns = (
    "date"       => 0,
    "openAsk"    => 1,
    "highAsk"    => 2,
    "lowAsk"     => 3,
    "closeAsk"   => 4,
    "openBid"    => 5,
    "highBid"    => 6,
    "lowBid"     => 7,
    "closeBid"   => 8,
    "EMAclose5" => 9,
    "EMAclose10"  => 10,
    "EMAclose20" => 11,
    "EMAclose40" => 12,
	"EMAclose60" => 13
);

# use strategy to do virtual trades
my %currentPostion =
  (    # Todo, need to make it be Array of hashes so that it can deal with futher more than 1 position numbers
    "number"     => 0,        #presently, it could only be 0 or 1;
    "type"       => undef,    # Long or Short
    "lots"       => 0,
    "PL"         => undef,    # in pips
    "openTime"   => undef,
    "closeTime"  => undef,
    "openPrice"  => undef,
    "closePrice" => undef
  );

my $initalBalance = $opt->initialmoney;     # initail capital
my $lotsFactor    = $opt->leveragefactor;
; # if the account balance is 200, than each tarde lot is 0.01; if the account balance is 400, then 0.02. So the lostFactor should be 200.
my %currentAccountInfo = ( "balance" => $initalBalance, "equity" => $initalBalance );
my %nextTradeDateActionItems = (
    "none"               => "0",
    "openLongPosition"   => "1",
    "openShortPosition"  => "2",
    "closeLongPosition"  => "3",
    "closeShortPosition" => "4"
);

my @tradePerformanceHistory = ();    #todo.  I would like a array of object rather than arrya of scale

open DEBUG, "> output/data/DEBUG_file_for_" . $opt->instrument;
open EQUITY, "> output/data/EQUITY_file_for_". $opt->instrument. ".csv";

open TRADERECORD, "> output/data/TRADERECORD_file_for_". $opt->instrument. ".csv";
print TRADERECORD "openTime\tcloseTime\toperateType\tpositionType\tlots\topenPrice\tclosePrince\tPips\tP/L". "\n";

my $oWkS;

&main();
close DEBUG;close EQUITY;close TRADERECORD;

#main process
sub main () {
    $oWkS = &loadData();
    &simulateTrade();
    &displayResult();
}

# load xml data from data file. Then return a Excel work sheet object to the caller
sub loadData {
    my $oExcel   = new Spreadsheet::ParseExcel;
    my $fileName = $data_dir . "/excel/" . $opt->instrument . "_" . $opt->period . ".xls";
    print "fileName = " . $fileName . ".\n";
    my $oBook = $oExcel->Parse($fileName);

    print "FILE  :", $oBook->{File},       "\n";
    print "COUNT :", $oBook->{SheetCount}, "\n";

    print "AUTHOR:", $oBook->{Author}, "\n" if defined $oBook->{Author};

    my $oWkS;

    $oWkS = $oBook->{Worksheet}[0];

    return $oWkS;
}

#update account equity if there is postion. Formular is equity = balance + unrealized P/L
sub updateEquity {
    my $rowId = $_[0];
    if ( $currentPostion{"number"} != 0 ) {
            my $openPrice               = $currentPostion{"openPrice"};
            my $type                    = $currentPostion{"type"};
            my $currentPeroidClosePrice = undef;
            my $PL                      = undef;
            if ( $type eq "Long" ) {
                $currentPeroidClosePrice = $oWkS->{Cells}[$rowId][ $columns{"closeBid"} ]->Value;
                $PL                      = ( $currentPeroidClosePrice - $openPrice ) * 10000.0;

            }
            else {
                $currentPeroidClosePrice = $oWkS->{Cells}[$rowId][ $columns{"closeAsk"} ]->Value;
                $PL                      = ( $openPrice - $currentPeroidClosePrice ) * 10000.0;
            }

            $currentAccountInfo{"equity"} = $currentAccountInfo{"balance"} + $PL * $currentPostion{"lots"} / 10;

        }
        else {
            #do nothing.
        }
}

#determine the action in the next trading period, according to some strategy
#now, My stretagy is :
#see the file description at top.

sub determineNextPeriodAction{
    my $rowId = $_[0];
    my $returnNextPeriodAction;
    my $EMA5  = $oWkS->{Cells}[$rowId][ $columns{ "EMAclose" . 5 } ]->Value;
    my $EMA10 = $oWkS->{Cells}[$rowId][ $columns{ "EMAclose" . 10 } ]->Value;
    my $EMA20  = $oWkS->{Cells}[$rowId][ $columns{ "EMAclose" . 20 } ]->Value;
    my $EMA40 = $oWkS->{Cells}[$rowId][ $columns{ "EMAclose" . 40 } ]->Value;
        if ( $EMA5 >= $EMA10 && $EMA10>=$EMA20 && $EMA20 >= $EMA40) {
            if ( $currentPostion{"number"} == 0 ) {
                $returnNextPeriodAction = $nextTradeDateActionItems{"openLongPosition"};
            }
        }
        elsif ( $EMA5 <= $EMA10 && $EMA10<=$EMA20 && $EMA20 <= $EMA40 ) {
            if ( $currentPostion{"number"} == 0 ) {
                $returnNextPeriodAction = $nextTradeDateActionItems{"openShortPosition"};
            }
        }
        else {    # this is a common case 
            $returnNextPeriodAction = $nextTradeDateActionItems{"none"};
        }
    return $returnNextPeriodAction;
    
}

#record the equity move . This subroutine will recode the move into a seperate log file
sub recordEquityLog{
   my $rowId = $_[0]; 
   print EQUITY "At the end of \t" . $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value . "\t".$currentAccountInfo{"equity"} . "\n";
}


#according to the passed in data ojbect, do the virtual trades
sub simulateTrade {
    my $nextTradeDateAction = $nextTradeDateActionItems{"none"};
    for ( my $iR = $oWkS->{MaxRow} ;$iR >= $oWkS->{MinRow} && $iR > ( $oWkS->{MaxRow} - $oWkS->{MaxRow} ) ; $iR--) {
        #do action determined by last execution.
        if ( $nextTradeDateAction ne $nextTradeDateActionItems{"none"} ) {
            &doTradingAction( $nextTradeDateAction, $iR );
            $nextTradeDateAction = $nextTradeDateActionItems{"none"}; #clear the next period action
        }
        # if the P/L reaches the limit or stop, close the postion
        &dailyPositionCloseWatcher($iR);

        #update account equity periodly
        &updateEquity($iR);

        &recordEquityLog($iR);

        #determine the action in the next trading period
        $nextTradeDateAction = &determineNextPeriodAction($iR);
    }
}

#a watcher that can close the position if P/L reaches the limit or stop protection
sub dailyPositionCloseWatcher {
    my $rowId = $_[0];
    if($currentPostion{"number"} > 0){
        my $currentRowOpenPrice;
        my $currentRowHighPrice;
        my $currentRowLowPrice;
        my $currentRowClosePrice;

        if ($currentPostion{"type"} eq "Long") {
            $currentRowOpenPrice = $oWkS->{Cells}[$rowId][ $columns{"openBid"} ]->Value;
            $currentRowHighPrice = $oWkS->{Cells}[$rowId][ $columns{"highBid"} ]->Value;
            $currentRowLowPrice = $oWkS->{Cells}[$rowId][ $columns{"lowBid"} ]->Value;
            $currentRowClosePrice = $oWkS->{Cells}[$rowId][ $columns{"closeBid"} ]->Value;
            my $maxProfitPips = ($currentRowHighPrice - $currentPostion{"openPrice"}) * 10000;
            my $minLossPips = ($currentRowLowPrice - $currentPostion{"openPrice"}) * 10000;
            # caculate loss first, because I don't know it it is limit price or stop price that is reached by currenty period
            if (-$minLossPips > $opt->stop || $maxProfitPips >  $opt->limit){ # close postion at STOP protection price
                my $closePrice;
                if(-$minLossPips > $opt->stop) {
                    $closePrice = $currentPostion{"openPrice"} - $opt->stop/10000.0;    
                }else{
                    $closePrice = $currentPostion{"openPrice"} + $opt->limit/10000.0;
                }
                        
        #todo openLongPosition
        #1. change the %currentPostion
        #2. change the %currentAccountInfo  ; it should update the equity value. But the equity value should be evaluated on each end of period. So here we could ignore it.

                print DEBUG "Action is closeLongPosition";
                my $PL         = ( $closePrice - $currentPostion{"openPrice"} ) * 10000.0;
                my $realPLinMoney = $PL * $currentPostion{"lots"}/10;
                my $logmsg =
                    "close the "
                  . $currentPostion{"type"}
                  . " postion at"
                  . $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value
                  . "; openTime is "
                  . $currentPostion{"openTime"}
                  . "; closePrice is "
                  . $closePrice
                  . "; openPrice is "
                  . $currentPostion{"openPrice"}
                  . "; microLots is "
                  . $currentPostion{"lots"}
                  . "; P/L is $PL pips" . "\n";
                print DEBUG $logmsg;
                print TRADERECORD $currentPostion{"openTime"}."\t".$oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value."\t"."closePosition"."\t". $currentPostion{"type"}. "\t". $currentPostion{"lots"}."\t".$currentPostion{"openPrice"}. "\t".$closePrice. "\t" . $PL. "\t". $realPLinMoney. "\n";

                #update balance and equity
                $currentAccountInfo{"balance"} = $currentAccountInfo{"balance"} + ( $PL * $currentPostion{"lots"} / 10 );
                $currentAccountInfo{"equity"} = $currentAccountInfo{"balance"};
                $currentPostion{"number"} -= 1;
                $currentPostion{"type"}       = undef;
                $currentPostion{"lots"}       = 0;
                $currentPostion{"PL"}         = undef;
                $currentPostion{"openTime"}   = undef;
                $currentPostion{"closeTime"}  = undef;
                $currentPostion{"openPrice"}  = undef;
                $currentPostion{"closePrice"} = undef;    
            }
             
        }else{ # short position
            $currentRowOpenPrice = $oWkS->{Cells}[$rowId][ $columns{"openAsk"} ]->Value;
            $currentRowHighPrice = $oWkS->{Cells}[$rowId][ $columns{"highAsk"} ]->Value;
            $currentRowLowPrice = $oWkS->{Cells}[$rowId][ $columns{"lowAsk"} ]->Value;
            $currentRowClosePrice = $oWkS->{Cells}[$rowId][ $columns{"closeAsk"} ]->Value;

            my $maxProfitPips = ($currentPostion{"openPrice"} - $currentRowLowPrice) * 10000;
            my $minLossPips = ( $currentPostion{"openPrice"} - $currentRowHighPrice) * 10000;
            # caculate loss first, because I don't know it it is limit price or stop price that is reached by currenty period

            if (-$minLossPips > $opt->stop || $maxProfitPips >  $opt->limit){ # close postion at STOP protection price
                my $closePrice;
                if(-$minLossPips > $opt->stop) {
                    $closePrice = $currentPostion{"openPrice"} + $opt->stop/10000.0;    
                }else{
                    $closePrice = $currentPostion{"openPrice"} - $opt->limit/10000.0;
                }

                print DEBUG "Action is closeShortPosition";
                my $PL         = ( $currentPostion{"openPrice"} - $closePrice ) * 10000.0;
                my $realPLinMoney = $PL * $currentPostion{"lots"}/10;
                my $logmsg =
                    "close the "
                  . $currentPostion{"type"}
                  . " postion at"
                  . $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value
                  . "; openTime is "
                  . $currentPostion{"openTime"}
                  . "; closePrice is "
                  . $closePrice
                  . "; openPrice is "
                  . $currentPostion{"openPrice"}
                  . "; microLost is "
                  . $currentPostion{"lots"}
                  . "; P/L is $PL pips" . "\n";
                print DEBUG $logmsg;
                print TRADERECORD $currentPostion{"openTime"}."\t".$oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value."\t"."closePosition"."\t". $currentPostion{"type"}. "\t". $currentPostion{"lots"}."\t".$currentPostion{"openPrice"}. "\t".$closePrice. "\t" . $PL. "\t". $realPLinMoney. "\n";

                #update balance
                $currentAccountInfo{"balance"} = $currentAccountInfo{"balance"} + ( $PL * $currentPostion{"lots"} / 10 );
                $currentAccountInfo{"equity"} = $currentAccountInfo{"balance"};
                $currentPostion{"number"} -= 1;
                $currentPostion{"type"}       = undef;
                $currentPostion{"lots"}       = 0;
                $currentPostion{"PL"}         = undef;
                $currentPostion{"openTime"}   = undef;
                $currentPostion{"closeTime"}  = undef;
                $currentPostion{"openPrice"}  = undef;
                $currentPostion{"closePrice"} = undef;


            }

        }
    }
}

#just to trades, without doing any strategy
sub doTradingAction {
    my $action      = $_[0];
    my $rowId       = $_[1];
    my $positionLot = ceil( $currentAccountInfo{balance} / $lotsFactor );    # unit : 0.01 lot = micro lot
    my $date        = $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value;

    if ( $action eq $nextTradeDateActionItems{"openLongPosition"} ) {
        print DEBUG "Action is openLongPosition. ";

        $currentPostion{"number"} += 1;

        $currentPostion{"type"}       = "Long";
        $currentPostion{"lots"}       = $positionLot;
        $currentPostion{"PL"}         = undef;
        $currentPostion{"openTime"}   = $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value;
        $currentPostion{"closeTime"}  = undef;
        $currentPostion{"openPrice"}  = $oWkS->{Cells}[$rowId][ $columns{"openAsk"} ]->Value;
        $currentPostion{"closePrice"} = undef;

        my $logmsg =
            "open a "
          . $currentPostion{"type"}
          . " postion at "
          . $currentPostion{"openTime"}
          . "; openPrice is "
          . $currentPostion{"openPrice"}
          . "microLots is "
          . $currentPostion{"lots"} . "\n";
        print DEBUG $logmsg;
        print TRADERECORD $currentPostion{"openTime"}."\t"."\t"."openPosition"."\t". $currentPostion{"type"}. "\t". $currentPostion{"lots"}."\t".$currentPostion{"openPrice"}. "\t\t\t". "\n";

    }
    elsif ( $action eq $nextTradeDateActionItems{"openShortPosition"} ) {
        print DEBUG "Action is openShortPosition";
        $currentPostion{"number"} += 1;
        $currentPostion{"type"}       = "Short";
        $currentPostion{"lots"}       = $positionLot;
        $currentPostion{"PL"}         = undef;
        $currentPostion{"openTime"}   = $oWkS->{Cells}[$rowId][ $columns{"date"} ]->Value;
        $currentPostion{"closeTime"}  = undef;
        $currentPostion{"openPrice"}  = $oWkS->{Cells}[$rowId][ $columns{"openBid"} ]->Value;
        $currentPostion{"closePrice"} = undef;

        my $logmsg =
            "open a "
          . $currentPostion{"type"}
          . " postion at"
          . $currentPostion{"openTime"}
          . "; openPrice is "
          . $currentPostion{"openPrice"}
          . "; microLots is "
          . $currentPostion{"lots"} . "\n";
        print DEBUG $logmsg;
        print TRADERECORD $currentPostion{"openTime"}."\t"."\t"."openPosition"."\t". $currentPostion{"type"}. "\t". $currentPostion{"lots"}."\t".$currentPostion{"openPrice"}. "\t\t\t". "\n";

    }
    elsif ( $action eq $nextTradeDateActionItems{"closeLongPosition"} ) {

    }
    elsif ( $action eq $nextTradeDateActionItems{"closeShortPosition"} ) {        

    }
    else {

        #do nothing
    }

}

##
# 1. show invest result in cmd
# 2. open google chart to show invest result
sub displayResult {
exec "google-chrome  http://ubuntu/forex-study/chart.pl"
}

